package moa.structure;

import java.io.*;
import java.util.*;

/**
 * <p>
 * This class implements the prefixed tree nodes used with the PSP
 * algorithm.
 * </p>
 * <p>
 * Each node contains a reference to its father, an Item (integer value) and the
 * references to its two type of childs (sameTransaction or
 * otherTransaction).
 * </p>
 *
 */
public class Node implements Cloneable,Comparable 
{ 
  /**<p>
   * Represents the support value for the sequence represented from the root
   * node to the actual node
   * </p>
   */
  private double support;

  /**<p>
   * Represents the depth of this node in the prefixed tree (0 for the root
   * node)
   * </p>
   */
  private int depth;

  /**<p>
   * The father Node reference
   * </p>
   */
  public Node father;

  /**<p>
   *  Boolean flag to know if the Node is in the same transaction than his father
   * </p>
   */
  public boolean isSame;

  /**<p>
   *  Set containing the sons that are in the same transaction than this node
   * </p>
   */
  
  private TreeSet<Node> sameTransaction;  // of type Node
  
  /**<p>
   *  Set containing the sons that are in an other transaction than this node
   * </p>
   */
  
  private TreeSet<Node> otherTransaction; // of type Node



  /**<p>
  * A collection used to contain all the sons of this node : same transaction and other transaction type
  * </p>
  */
  public Vector<Node> allSons;

  /**<p>
  * The item contained in the node
  * </p>
  */
  private int item;
  
  private SequenceApparition sequenceApparition;

  
  
//******************************************************************************************************************************************************************
//Constructors of the class Node
//******************************************************************************************************************************************************************

  /**<p>
   * Create a new Root
   *</p>
   */
  public Node() 
  {
	  item = -1;
	  father = null;
	  depth = 0;
	  isSame = true;
	  support = 0;

	  sameTransaction = new TreeSet<Node>();
	  otherTransaction = new TreeSet<Node>();
	  sequenceApparition = null;
  }

  /**<p>
   * Create a new Node with the given item
   *</p>
   */
  public Node(int it)
  {
	  	item = it;
	    isSame = true;
	    support = 0;

	    sameTransaction = new TreeSet<Node>();
	    otherTransaction = new TreeSet<Node>();
	    sequenceApparition = null;
  }
  
  
  
//******************************************************************************************************************************************************************
//Accessors of the class Node
//******************************************************************************************************************************************************************

  /**<p>
   * Accessor for the sequenceApparition of this node
   * </p>
   */
  public SequenceApparition getSequenceApparition()
  {
	  return sequenceApparition;
  }
  
  
  /**<p>
   * Accessor to modify the sequenceApparition
   * </p>
   */
  public void setSequenceApparition(SequenceApparition sequenceApparition)
  {
	  this.sequenceApparition = sequenceApparition;
  }

  /**<p>
   * Accessor for the father of the current node
   * </p>
   */
  public Node getFather() 
  {
	  return father;
  } 
  
  /**<p>
   * Accessor to set the father
   * </p>
   */
  public void setSupport(Node father) 
  {
	 this.father = father;
  }

  /**<p>
   * Accessor for the support value
   * </p>
   */
  public double getSupport() 
  {
	  return support;
  } 

  /**<p>
   * Accessor to modify the support value
   * </p>
   */
  public void setSupport(double _support) 
  {
	  support = _support;
  }
  
  /**<p>
   * Accessor for the same transaction set
   * </p>
   */
  public Set<Node> getSameTrans() 
  {
	  return sameTransaction;
  }

  /**<p>
   * Accessor to modify the set containing the children who are in the same transaction
   *</p>
   */
  public void setSameTrans(TreeSet<Node> _set) 
  {
	  sameTransaction = _set;
  }

  /**<p>
   * Accessor for the other transaction set
   * </p>
   */
  public Set getOtherTrans() 
  {
	  return otherTransaction;
  }

  /**<p>
   * Accessor to modify the other transaction set
   *</p>
   */
  public void setOtherTrans(TreeSet<Node> _set)
  {
	  otherTransaction = _set;
  }

  /**<p>
   * Accessor for the Item integer value
   * </p>
   */
  public int getItem() 
  {
	 return item;
  } 
  
  public boolean isSame()
  
  {
	  return isSame;
  }

  /**<p>
   * Accessor to modify the Item object
   * </p>
   */
  public void setItem(int _item)
  {
    item = _item;
  }
  
  /**<p>
   * Accessor to get the depth value of this Node in the Prefixed Tree
   * </p>
   */
  public int getDepth() 
  {
	  return depth;
  } 

  /**<p>
   * Accessor to modify depth value of the Node
   * </p>
   */
  public void setDepth(int _depth) 
  {
    depth = _depth;
  } 
  
//******************************************************************************************************************************************************************
//Methods of the class Node
//******************************************************************************************************************************************************************

 
  /**<p>
   * Increments the support
   *</p>
   */
  public void incSupport() 
  {
	  support++;
  }
  

  /**<p>
   * @return A set of items corresponding to the nodes that are in the same transaction than the current one
   *</p>
   */
  public Set<Integer> getSameTransItems() 
  {
	  	Set<Integer> sameTransactionItems = new TreeSet<Integer>();
	    Iterator tmpIt = sameTransaction.iterator();
	    while (tmpIt.hasNext()) 
	    {
	    	Node nTmp = (Node) tmpIt.next();
		    sameTransactionItems.add(new Integer(nTmp.getItem()));
	    }
	    return sameTransactionItems;
  }

  /**<p>
   * @return the node contained in the children who are in the same transaction which item is "it"
   * </p>
   */
  public Node getSameTransSon(int it)
  {
	  	Iterator tmpIt = sameTransaction.iterator();
	    while (tmpIt.hasNext()) 
	    {
	    	Node nTmp = (Node) tmpIt.next();
		    if(nTmp.getItem() == it)
		    {
		    	return nTmp;
		    }
	    }
	    return null;
  }

  /**<p>
   * @return the node contained in the children who are in another transaction which item is "it"
   * </p>
   */
  public Node getOtherTransSon(int it)
  {
	  Iterator tmpIt = otherTransaction.iterator();
	  while (tmpIt.hasNext()) 
	  {
		  Node nTmp = (Node) tmpIt.next();
		  if(nTmp.getItem() == it)
		  {
			  return nTmp;
		  }
	   	}
	   return null;
  }


  
  /**<p>
   * @return A set of items corresponding to the nodes that are in another than the current one
   *</p>
   */
  public Set<Integer> getOtherTransItems() 
  {
	  Set<Integer> otherTransactionItems = new TreeSet<Integer>();
	  Iterator tmpIt = otherTransaction.iterator();
	  while (tmpIt.hasNext()) 
	  {
		  Node nTmp = (Node) tmpIt.next();
	      otherTransactionItems.add(new Integer(nTmp.getItem()));
	  }
	  return otherTransactionItems;
  }


  /**<p>
   * @return a vector containing all the sons of the current node (same transaction & other transaction)
   * </p>
   */
  public Vector<Node> getAllSons() 
  {
	  allSons = new Vector<Node>();
	  if (sameTransaction != null) 
	  {
		  allSons.addAll(sameTransaction);
	  }
	  if (otherTransaction != null) 
	  {
	      allSons.addAll(otherTransaction);
	  }
	  return allSons;
  }

  /**<p>
   * @return the first Node contained in the Vector allsons (this one contains both same transaction and other transaction nodes)
   * </p>
   */
  public Node getNextSon() 
  {
	  return (allSons.elementAt(0));
  }

  /**<p>
   * Adds a new child into the other transaction set
   * @param _node is the new child
   * </p>
   */
  public void addInOther(Node _node) 
  {
	  _node.father = this;
	  _node.setDepth(depth + 1);
	  _node.isSame = false;
	  _node.support = 0;

	  // Memory saving, otherTransaction is instancied just before adding the first element
	  if (otherTransaction == null)
	  {
		  otherTransaction = new TreeSet<Node>();
	      otherTransaction.add(_node);
	  }
	  else 
	  {
	      otherTransaction.add(_node);
	  }
  }

  /**<p>
   * Adds a new child into the same transaction set
   * @param _node is the new child
   * </p>
   */
  public void addInSame(Node _node) 
  {
	 _node.father = this;
	 _node.setDepth(depth + 1);
	 _node.isSame = true;
	 _node.support = 0;

	 // Memory saving, no object creation before real use
	 if (sameTransaction == null) 
	 {
		 sameTransaction = new TreeSet<Node>();
	     sameTransaction.add(_node);
	 }
	 else 
	 {
	     sameTransaction.add(_node);
	 }
  } 

  /**<p>
   * Deletes the selected node from the other transaction set
   * @param _node is the Node to be deleted
   * </p>
   */
  public void deleteInOther(Node _node) 
  {
	  if (otherTransaction != null) 
	  {
		  otherTransaction.remove(_node);
	      _node = null;
	  }
  } 

  /**<p>
   * Deletes the selected node from the same transaction set
   * @param _node is the Node to be deleted
   * </p>
   */
  public void deleteInSame(Node _node) 
  {
	  if (sameTransaction != null) 
	  {
		  sameTransaction.remove(_node);
	      _node = null;
	  }
  }

  /**<p>
   * Makes a test to check if the current Node is a leaf
   * @return a boolean (true if leaf, false if not)
   * </p>
   */
  public boolean isLeaf() 
  {
	  return(sameTransaction.size() == 0 && otherTransaction.size() == 0);
  } 

  /**<p>
   * Does a simple set iteration to get the brothers of the Node that are of
   * same transaction type
   * @return a Set with the brothers of the Node
   * </p>
   */
  public Set<Node> getSameBrothers() 
  {
	  if (father.getSameTrans() != null) 
	  {
	      Set bros = father.getSameTrans();
	      return bros;
	  }
	  return null;
  }


  /**<p>
   * Does a simple set iteration to get the brothers of the Node that are of
   * other transaction type
   * @return a Set with the brothers of the Node
   * </p>
   */
  public Set<Node> getOtherBrothers() 
  {
	   if (father.getOtherTrans() != null) 
	   {
		   Set bros = father.getOtherTrans();
	       return bros;
	   }
	   return null;
  }

  /**<p>
   *@return a set containing the items of the brothers of the current node which are in "other transaction"
   * </p>
   */
  public Set<Integer> getOtherBrothersItems() 
  {
	  if (father.getOtherTrans() != null) 
	  {
		  Set<Integer> bros = father.getOtherTransItems();
		  return bros;
	  }
	  return null;
  } 

  /**<p>
   *@return a set containing the items of the brothers of the current node which are in "same transaction"
   * </p>
   */
  public Set<Integer> getSameBrothersItems() 
  {
	  if (father.getSameTrans() != null) 
	  {
		  Set<Integer> bros = father.getSameTransItems();
	      return bros;
	  }
	  return null;
  }

  /**<p>
   * Does a simple test to see if the father of this node is in the same
   * transaction
   * @return a boolean with true value if the father is in the same
   * transaction
   * </p>
   */
  public boolean ifFatherSame() 
  {
    return isSame;
  }


  /**<p>
   * @return the longer itemset containing the item of this node
   * </p>
   */
  public ItemSet readSameItemSet() 
  {
	  Node actual = this;
	  ItemSet is = new ItemSet();
	  is.addItem(actual.getItem());

	  while (actual.father.father != null && actual.isSame) 
	  {
		  actual = actual.father;
	      is.addItemFront(actual.getItem());
	  }
	  return is;
  }

	
  /**<p>
   * Does a backward generation of the sequence starting from this Node and up
   * to the root Node
   * @return a Sequence with backward generated sequence
   * </p>
   */
  public SequenceInstance readSequence() 
  {
	    SequenceInstance seq = new SequenceInstance();
	    Node actual = this;
	    // We add our generating node to the
	    // sequence object, this is important
	    // to optimize our tree generation

	    ItemSet is = new ItemSet(item);

	    // Safety check for root node
	    if (depth == 0) 
	    {
	      return seq;
	    }

	    /* Backward recursivity */
	    while (actual.father != null) 
	    {

	      boolean testSame = actual.isSame;
	      actual = actual.father;

	      // The father is not in the same transaction
	      if (!testSame) 
	      {
	        seq.addFront(is);
	        is = new ItemSet();
	      }

	      if (actual.father != null) 
	      { 
	        is.addItemFront(actual.getItem());
	      }
	    }
	    // This is the last itemSet to be added to the Sequence
	    // before the root
	    if (is.getSize() != 0) 
	    {
	      seq.addFront(is);
	    }
	    return seq;
	}


//******************************************************************************************************************************************************************
//Other methods of the class Node
//******************************************************************************************************************************************************************

  /**<p> 
   * This method is called in order to duplicate a node : it is faster than instanciating a new one during the
   * candidate generation
   * @return the cloned node
   * </p>
   */
  public Object clone() 
  {
	  Object node = null;
	  try 
	  {
	      node = (Node)super.clone();
	      ((Node) node).setSameTrans((TreeSet<Node>)((TreeSet<Node> )this.getSameTrans()).clone());
	      ((Node) node).setOtherTrans((TreeSet<Node>)((TreeSet <Node>)this.getOtherTrans()).clone());
	      ((Node)node).getSameTrans().clear();
	      ((Node)node).getOtherTrans().clear();
	      
	  }
	  catch (CloneNotSupportedException e) 
	  {
		  throw new InternalError(e.toString());
	  }
	  return node;
  }
  
  /**
   * The compareTo function is implemented to help sorting the Nodes into the Tree
   * (0 1 2 ... n) etc..
   * @param n Represents a Node
   * @return -1 if the actual Node item is smaller than n's Node item, 0 if equal and 1 if bigger
   */
  public int compareTo (Object n)
  {
	  if (item < ((Node)n).getItem()) 
	  {
		  return -1;
	  }
	  else 
	  {
		  if (item > ((Node) n).getItem())
		  {
			  return 1;
		  }
		  return 0;
	  }
  }
  
  /**<p>
   * We consider that two nodes are equals if they belong to a same path from the root to them
   * </p>
   */
  public boolean equals(Object o)
  {
	  if ( ((Node)o).depth != depth || ((Node)o).item != item || ((Node)o).isSame != isSame )
		  return false;
	  Node n1 = this;
	  Node n2 = (Node) o;
	  for(int i=0; i<depth; i++)
	  {
		  if (n1.item != n2.item || n1.isSame!= n2.isSame)
			  return false;
		  n1 = n1.getFather();
		  n2 = n2.getFather();
	  }
	  return true;
  }
  

  /**<p>
   * This methode return a string with the item of the current node and the sequence it belongs to in order to visualize the node
   * in the prefixed tree
   * </p>
   */
  public String toString() 
  {
	  if (isSame())
	  {
		  return (getItem() + " : same");
	  }
	  else
	  {
		  return (getItem() + " : other");
	  }
  }
  
}
